#include "ExternSort.h"
#include "Registers.h"
#include "datatypes.h"

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <map>

using std::string;
using std::vector;
using std::map;
using std::cout;
using std::endl;

typedef map<AuxiliaryRegister, int> BufferSorted;

/********************************************************************************************/
ExternSort::ExternSort(string nameIn, string nameOut)
{
	fileIn = new AuxiliaryFile( nameIn );
	fileIn->openFile();
	this->nameIn = nameIn;
	this->nameOut = nameOut;
}
/********************************************************************************************/
ExternSort::~ExternSort()
{
	fileIn->closeFile();
	fileIn->removeFile();
	delete fileIn;
}
/********************************************************************************************/
bool ExternSort::DivideSort(void)
{	
	unsigned int totalRegisters = 0;
	unsigned int totalProccesedFiles = 0;
	unsigned int iterations = 0;
	string nameFile;
	char str[30];
	BufferSorted bufferSorted;
	tRegAuxiliary sAuxReg;
	AuxiliaryRegister cAuxReg; 
	AuxiliaryFile* fileSort;
	
	memset( str, 0, sizeof(str));
	
	if ( fileIn->size() < fileIn->sizeData() ) //no tengo datos para procesar
		return false;
	
	totalRegisters = fileIn->size()/fileIn->sizeData();
	iterations = totalRegisters/QUANTITY_REG_FILE_DIVIDE; //si da cero es una sola iter
	
	if ( ( totalRegisters % QUANTITY_REG_FILE_DIVIDE ) > 0 ) //cuando da cero suma la unica aca
		iterations++; //necesito una iteracion mas para los que estan en el resto de la div
	
	for(unsigned int j=0; j<iterations;j++)
	{
		sprintf(str, "%d", j);
		nameFile = str; nameFile+= ".txt";
		fileSort = new AuxiliaryFile(nameFile);
		fileSort->createFile();
		fileSort->openFile();
		listNames.push_back(nameFile);
		
		//leo la cantidad de registros estipulada y ordeno en memoria
		for(int i=0; ((i< QUANTITY_REG_FILE_DIVIDE) && (!fileIn->eof())); i++)
		{
			fileIn->readRegister(sAuxReg);
			cAuxReg.setData( sAuxReg );
			bufferSorted[ cAuxReg ] = i;
		}
		
		//escribo el archivo temporal ordenado
		while( !bufferSorted.empty() )
		{
			cAuxReg = (*bufferSorted.begin()).first;
			bufferSorted.erase(bufferSorted.begin());
			fileSort->addRegister(cAuxReg.getData());
		}
		fileSort->closeFile();
		delete fileSort;
		totalProccesedFiles++;
	}

	return true; // hizo lo que tenia que hacer
}
/********************************************************************************************/
bool ExternSort::Merge(void)
{
	int MAX_FILES = ( listNames.size() > QUANTITY_OPEN_FD )? QUANTITY_OPEN_FD : listNames.size();
	static int iNameTmp = 0;
	char cNameTmp[200];
	
	tRegAuxiliary sAuxReg[MAX_FILES];
	AuxiliaryRegister cAuxReg[MAX_FILES], firstAuxReg;
	BufferSorted bufferSorted;
	AuxiliaryFile* tmpFile;
	AuxiliaryFile* fileSorted[MAX_FILES];
	int cant_files=0, i, j;
	bool _end = false;
	
	memset(cNameTmp, 0, sizeof(cNameTmp));
	sprintf(cNameTmp, "%d", iNameTmp);
	string nameTmp = cNameTmp;

	/*if ( listNames.size() == 0 ) //hubo error en DivideSort
		return false;*/
		
//	cout<<"Size: "<<listNames.size()<<endl;
		
	while( !_end )
	{
		for(int l = 0 ; l<MAX_FILES;l++)
			fileSorted[cant_files]=NULL;
			
		tmpFile = new AuxiliaryFile(nameTmp);
		tmpFile->createFile();
		tmpFile->openFile();
		
		//cout <<"MAX_FILES = "<<MAX_FILES<<endl;
		
		for( cant_files = 0; cant_files< MAX_FILES  ; cant_files++ )
		{
			if( listNames.empty() ){
				_end = true;
				break;
			}
			fileSorted[cant_files] = new AuxiliaryFile(*listNames.begin()); // abro los #MAX_FILES primeros archivos
	//		cout<<"Name: "<<fileSorted[cant_files]->getName()<<endl;
			listNames.erase(listNames.begin());//borro los nombres de archivo que ya use
			fileSorted[cant_files]->openFile();
		}
		
		//cout <<"cant_files = "<<cant_files<<endl;
		for( i = 0; i< cant_files; i++ ) //leo un registro de todos los archivos abiertos y los cargo en el buffer
		{
			if (fileSorted[i] == NULL )
				continue;
				
			fileSorted[i]->readRegister(sAuxReg[i]);
			//cout<<"Data"<<sAuxReg[i]._Id<<endl;
			cAuxReg[i].setData( sAuxReg[i]);
			bufferSorted[cAuxReg[i]] = i;
			//cout<<"Inserto Id: "<< sAuxReg[i]._Id<<endl;
		}	
		
		while( !bufferSorted.empty() ) // descargo todo el buffer ordenado en el archivo temp_final
		{
			firstAuxReg = (*bufferSorted.begin()).first;
			j = (*bufferSorted.begin()).second;
			bufferSorted.erase(bufferSorted.begin());//lo saco del buffer

			if( (fileSorted[j]!=NULL) && (fileSorted[j]->eof()) ) // reviso si llegue a fin de archivo de alguno, si si cargo otro en la lista
			{
				fileSorted[j]->closeFile();
				fileSorted[j]->removeFile();
				delete fileSorted[j];
				fileSorted[j] = NULL;
			}
			else
			{
				//cout<<"Name: "<<fileSorted[j]->getName()<<endl;
				fileSorted[j]->readRegister(sAuxReg[j]);//leo el siguiente, del archivo que saque el registro
				cAuxReg[j].setData( sAuxReg[j]);
				bufferSorted[cAuxReg[j]] = j; //agrego el nuvo elemento al buffer
				tmpFile->addRegister(firstAuxReg.getData()); // escribo en el archivo temporal el dato				
			}
		}
		tmpFile->closeFile();
		if( !_end)
		{
			iNameTmp++;
			listNames.push_back(nameTmp);
			memset(cNameTmp, 0, sizeof(cNameTmp));
			sprintf(cNameTmp, "%d", iNameTmp);
			nameTmp = cNameTmp;
		}
		else
		{
			tmpFile->renameFile(nameOut);
			delete tmpFile;
		}
	}
	return true;
}
/********************************************************************************************/
